package me.wlin.leaf;

import lombok.extern.slf4j.Slf4j;
import me.wlin.leaf.exception.ClockBackwardException;

/**
 * twitter的Id生产器算法。产生的id格式为：
 * Long型ID , 共 64 位。
 * 第一位未使用（可作为long的符号位）+ 41位为毫秒级时间 + 10位服务器ID（WorkerID）+ 12位该毫秒内的当前毫秒内的计数
 */
@Slf4j
public class TwitterIdWorker {

    private final static long workerIdBits       = 10L;    //机器id的最大长度
    private final static long sequenceBits       = 12L;   //序列号的长度
    private final static long workerIdShift      = sequenceBits; // workerId 位移数
    private final static long timestampLeftShift = sequenceBits + workerIdBits;// 时间戳位移数

    private final long  workerId;
    public final static long maxWorkerId        = -1L ^ -1L << workerIdBits;

    private long sequence  = 0L;    //序列号，如果时间戳（毫秒级）变化就清零
    private final static long sequenceMask       = -1L ^ -1L << sequenceBits;

    private final static long epoch = 1361753741828L;   //起始时间点
    private long lastTimestamp      = -1L;
    
    private static TwitterIdWorker instance;    //系统唯一的ID产生器实例
    
    public static TwitterIdWorker getInstance(long workerId){
        if(instance == null){
            synchronized (TwitterIdWorker.class) {
                if(instance == null)
                    instance = new TwitterIdWorker(workerId);
            }
        }
        if(instance.getWorkerId() != workerId){
            throw new IllegalStateException("can not get instance with id "+workerId+" ,Worker initialized，workerId is "+ instance.getWorkerId());
        }
        return instance;
    }
    
    private TwitterIdWorker(final long workerId) {
        super();
        if (workerId > maxWorkerId || workerId < 0) {
            throw new IllegalArgumentException(String.format(
                "worker Id can't be greater than %d or less than 0", maxWorkerId));
        }
        this.workerId = workerId;
    }

    public synchronized long nextId() {
        long timestamp = this.timeGen();
        
        if (this.lastTimestamp == timestamp) {
            this.sequence = (this.sequence + 1) & sequenceMask;
            if (this.sequence == 0) {
            	//这里如果一毫秒内计数器已经用完了所有的计数，则一直等到下一毫秒再重新获取新的时间戳
                log.info("sequence exhausted... wait next millis...");
                timestamp = this.tilNextMillis(this.lastTimestamp);
            }
        } else {
            this.sequence = 0;
        }
        
        //运行期间时间不能倒退
        if (timestamp < this.lastTimestamp) {
            try {
                throw new ClockBackwardException(String.format(
                    "Clock moved backwards.  Refusing to generate id for %d milliseconds",
                    this.lastTimestamp - timestamp));
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        this.lastTimestamp = timestamp;
        long nextId = ((timestamp - epoch << timestampLeftShift))
                      | (this.getWorkerId() << workerIdShift) | (this.sequence);

        if(log.isDebugEnabled()) {
            log.debug("nextId:{} ,timestamp:{} ,timestampLeftShift:{} ,workerId:{} ,sequence:",
                    nextId, timestamp, timestampLeftShift,  sequence);
        }

        return nextId;
    }

    private long tilNextMillis(final long lastTimestamp) {
        long timestamp = this.timeGen();
        while (timestamp <= lastTimestamp) {
            timestamp = this.timeGen();
        }
        return timestamp;
    }

    private long timeGen() {
        return System.currentTimeMillis();
    }


	public long getWorkerId() {
		return workerId;
	}
}